package com.yvan.spring.autoconfig;

import com.yvan.spring.LocalOrRemote;
import com.yvan.spring.LocalService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.util.Map;

@Configuration
public class SpringCloudAutoConfiguration implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Bean
    public BeanPostProcessor beanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                Class<?> objClz = bean.getClass();
                if (org.springframework.aop.support.AopUtils.isAopProxy(bean)) {
                    objClz = org.springframework.aop.support.AopUtils.getTargetClass(bean);
                }

                for (Field field : objClz.getDeclaredFields()) {
                    LocalOrRemote lr = field.getAnnotation(LocalOrRemote.class);
                    if (lr != null) {
                        Class type = field.getType();

                        Map m = applicationContext.getBeansOfType(type);
                        Object tb = null;
                        for (Object o : m.values()) {
                            if (tb == null) tb = o;
                            if (o instanceof LocalService) {
                                tb = o;
                                break;
                            }
                        }
                        if (tb == null) {
                            throw new RuntimeException(objClz.getName() + "." + field.getName() + " implement not exists!!");
                        }

                        try {
                            field.setAccessible(true);
                            field.set(bean, tb);
                            field.setAccessible(false);
                        } catch (Exception e) {
                            throw new BeanCreationException(beanName, e);
                        }
                    }
                }
                return bean;
            }

            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                return bean;
            }
        };
    }
}
